﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Hiro.ActivationPoints.Interfaces;
using Hiro.Interfaces;

namespace Hiro.ActivationPoints
{
    /// <summary>
    /// Represents an extension class that converts a type into an equivalent <see cref="IActivationPoint"/>.
    /// </summary>
    public static class TypeExtensions
    {
        /// <summary>
        /// Converts a type into an activation point.
        /// </summary>
        /// <param name="targetType">The target type to be converted.</param>
        /// <returns>A <see cref="ITypeActivationPoint"/>./returns>
        public static ITypeActivationPoint ToActivationPoint(this Type targetType)
        {
            return targetType.ToActivationPoint(new ArgumentResolver());
        }

        /// <summary>
        /// Converts a type into an activation point.
        /// </summary>
        /// <param name="targetType">The target type to be converted.</param>
        /// <param name="argumentResolver">The resolver that will be used to determine which dependencies should be applied to each constructor argument.</param>
        /// <returns>A <see cref="ITypeActivationPoint"/>./returns>
        public static ITypeActivationPoint ToActivationPoint(this Type targetType, IArgumentResolver argumentResolver)
        {
            return targetType.ToActivationPoint(argumentResolver, null);
        }        

        /// <summary>
        /// Converts a type into an activation point.
        /// </summary>
        /// <param name="targetType">The target type to be converted.</param>
        /// <param name="argumentResolver">The resolver that will be used to determine which dependencies should be applied to each constructor argument.</param>
        /// <param name="propertyResolver">The property resolver that will be used to determine which dependencies should be applied to each property injection operation.</param>
        /// <returns>A <see cref="ITypeActivationPoint"/>./returns>
        public static TypeActivationPoint ToActivationPoint(this Type targetType, IArgumentResolver argumentResolver, 
            IPropertyResolver propertyResolver)
        {
            // Register the type with the dependency map
            var composite = new TypeActivationPoint(targetType);
            foreach (var constructor in targetType.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
            {
                // Each constructor is an activation point that
                // maps to a single dependency
                var activationPoint = new ConstructorActivationPoint(constructor, argumentResolver);
                composite.ConstructorActivationPoints.Add(activationPoint);
            }

            if (propertyResolver == null)
                return composite;

            foreach (var property in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                // Ignore readonly properties
                if (!property.CanWrite)
                    continue;

                var propertyActivationPoint = new PropertyActivationPoint(property, propertyResolver);
                composite.PropertyActivationPoints.Add(propertyActivationPoint);
            }

            return composite;
        }
    }
}
